package io.wins.vertx;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.CompositeFuture;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Future;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.wins.vertx.core.CodelistManager;
import io.wins.vertx.core.SingletonVertx;
import io.wins.vertx.database.DataSourceFactory;
import io.wins.vertx.database.JdbcExecutor;
import io.wins.vertx.utils.JsonUtils;
import io.wins.vertx.utils.Runner;
import io.wins.vertx.verticle.CarouselVerticle;
import io.wins.vertx.verticle.ClassVerticle;
import io.wins.vertx.verticle.CommentVerticle;
import io.wins.vertx.verticle.ConcertVerticle;
import io.wins.vertx.verticle.CustomerVerticle;
import io.wins.vertx.verticle.GoodsVerticle;
import io.wins.vertx.verticle.PurchaseVerticle;
import io.wins.vertx.verticle.RoleVerticle;
import io.wins.vertx.verticle.RootVerticle;
import io.wins.vertx.verticle.SeatVerticle;
import io.wins.vertx.verticle.UserVerticle;
import io.wins.vertx.web.Server;

public class MainVerticle extends AbstractVerticle {
    private static Logger logger = LoggerFactory.getLogger(MainVerticle.class);

    public static void main(final String[] args) {
        MainVerticle.reconfigLoggingFramework("io.vertx.core.logging.Log4j2LogDelegateFactory");
        final URL filePath = MainVerticle.class.getClassLoader().getResource("config.json");
        try {
            final JsonObject config;
            final DeploymentOptions options = new DeploymentOptions();
            config = JsonUtils.readFromFile(new File(filePath.getFile()));
            options.setConfig(config);
            Runner.run(MainVerticle.class, options);
        } catch (final FileNotFoundException e) {
            MainVerticle.logger.error("Failed to load configuration file on path: " + filePath);
        }
    }

    private static void reconfigLoggingFramework(final String vertxLoggerDelegateFactory) {
        System.setProperty("vertx.logger-delegate-factory-class-name", vertxLoggerDelegateFactory);
        MainVerticle.logger.debug("Reconfig logging framework:" + vertxLoggerDelegateFactory);
        LoggerFactory.initialise();
        LoggerFactory.removeLogger(MainVerticle.class.getCanonicalName());
        MainVerticle.logger = LoggerFactory.getLogger(MainVerticle.class);
    }

    @Override
    public void start() throws Exception {
        SingletonVertx.init(this.vertx);
        DataSourceFactory.INSTANCE.init(this.config().getJsonObject("db"));
//        CacheManager.getInstance().init(this.config().getJsonObject("redis"));
//        vertx.deployVerticle(Server.class.getName(), new DeploymentOptions().setConfig(config()));
        this.deployInfrastructureVerticles()
            .compose(jr -> {
                Future<String> deployWorkerFuture = Future.future();
                deployWorkerFuture.complete();
                return deployWorkerFuture;
            })
            .compose(jr -> {
                return this.deployApplicationVerticles();
            })
            .compose(jr -> {
                MainVerticle.logger.info("Deploying Server...");
                final Future<String> serverFuture = Future.future();
                this.vertx.deployVerticle(Server.class.getName(), new DeploymentOptions().setConfig(this.config()),
                    r -> {
                        if (r.succeeded()) {
                            logger.info("Server deployed.");
                            serverFuture.complete();
                        } else {
                            serverFuture.fail(new RuntimeException("Error in deploying server", r.cause()));
                        }
                    });
                return serverFuture;
            });
    }


    private CompositeFuture deployInfrastructureVerticles() {
        MainVerticle.logger.info("Deploying infrastructure verticles.");
        final List<Future> infrastructureVerticles = new ArrayList<>();

        MainVerticle.logger.info("Deploying jdbc verticle.");
        final Future<String> jdbcFuture = Future.future();
        final Future<String> codelistFuture = Future.future();

        this.vertx.deployVerticle(new JdbcExecutor(), new DeploymentOptions()
            .setConfig(this.config()).setWorker(true).setMultiThreaded(true), jdbcFuture);

        jdbcFuture.setHandler(r -> {
            CodelistManager.CODE_LIST.init(this.vertx, codelistFuture);
        });

        infrastructureVerticles.add(codelistFuture);
        return CompositeFuture.all(infrastructureVerticles);
    }

    private CompositeFuture deployApplicationVerticles() {
        MainVerticle.logger.info("Deploying application verticles.");
        final List<Future> applicationVerticles = new ArrayList<>();
        final Future<String> userFuture = Future.future();
        this.vertx.deployVerticle(UserVerticle.class.getName(), userFuture.completer());
        applicationVerticles.add(userFuture);

        final Future<String> concertFuture = Future.future();
        this.vertx.deployVerticle(ConcertVerticle.class.getName(), concertFuture.completer());
        applicationVerticles.add(concertFuture);

        final Future<String> goodsFuture = Future.future();
        this.vertx.deployVerticle(GoodsVerticle.class.getName(), goodsFuture.completer());
        applicationVerticles.add(goodsFuture);

        final Future<String> roleFuture = Future.future();
        this.vertx.deployVerticle(RoleVerticle.class.getName(), roleFuture.completer());
        applicationVerticles.add(roleFuture);

        final Future<String> rootFuture = Future.future();
        this.vertx.deployVerticle(RootVerticle.class.getName(), rootFuture.completer());
        applicationVerticles.add(rootFuture);

        final Future<String> carouselFuture = Future.future();
        this.vertx.deployVerticle(CarouselVerticle.class.getName(), carouselFuture.completer());
        applicationVerticles.add(carouselFuture);

        final Future<String> classFuture = Future.future();
        this.vertx.deployVerticle(ClassVerticle.class.getName(), classFuture.completer());
        applicationVerticles.add(classFuture);

        final Future<String> seatFuture = Future.future();
        this.vertx.deployVerticle(SeatVerticle.class.getName(), seatFuture.completer());
        applicationVerticles.add(seatFuture);

        final Future<String> commentFut = Future.future();
        this.vertx.deployVerticle(CommentVerticle.class.getName(), commentFut.completer());
        applicationVerticles.add(commentFut);

        final Future<String> custFut = Future.future();
        this.vertx.deployVerticle(CustomerVerticle.class.getName(), custFut.completer());
        applicationVerticles.add(custFut);

        final Future<String> purchaseFut = Future.future();
        this.vertx.deployVerticle(PurchaseVerticle.class.getName(), purchaseFut.completer());
        applicationVerticles.add(purchaseFut);

        return CompositeFuture.all(applicationVerticles);
    }
}